[小ネタ] Drupal 8でALBのヘルスチェックが失敗しているときの対応
AWS事業本部 梶原@福岡オフィスです。
Drupal 8のサーバ構築する機会があったのですが、気が付いたらALBがターゲットのヘルスチェックに失敗していて、 「The provided host name is not valid for this server.」 のエラーメッセージがでていたんですが、対応に小ハマりしたのでやったことをまとめます
構成
CloudFront => ALB => EC2(Apache+PHP+Drupal) のWebサイトです。
EC2のWebサーバのIPアドレスは10.0.0.1 とします。
エラーの状況
- ALBのヘルスチェックがDrupalのインストール前は正常に行えていた。
- ヘルスチェックのパスは"/", HTTPステータスは200
- TargetGroupのステータスはUnhealthy状態
調査
何はともあれ、エラーが起きたときはログを確認します。
/var/log/httpd/access_log
10.0.CCC.111 - - [XX/May/2019:14:55:34 +0900] "GET / HTTP/1.1" 400 52 "-" "ELB-HealthChecker/2.0" 10.0.CCC.222 - - [XX/May/2019:14:55:35 +0900] "GET / HTTP/1.1" 400 52 "-" "ELB-HealthChecker/2.0" ※AAA.BBB.CCC.111, AAA.BBB.DDD.222はALBのアドレスです
あれ?サイトは普通に表示できてるのになんでエラーコードが400? 確認のためにローカルからチェックしてみます。
$ curl http://localhost/ The provided host name is not valid for this server. $ curl -I http://localhost/ HTTP/1.1 400 Bad Request
確かに400ですね。ただ、ここで、
「The provided host name is not valid for this server.」
とあるので、ホスト名の問題であろうことに気づきます。 クラウドフロントからはHostヘッダが送られているので、正常にアクセスできているとふみました。 ですので、Hostヘッダ付きでローカルからリクエストしてみます。
$ curl -I -H 'Host:www.exsample.com' http://localhost/ HTTP/1.1 200 OK
さて、原因はほぼ判明しました。CloudFrontからは通常のホスト名でのアクセスで、Hostヘッダが転送されているので正常に応答が返っていると思われます。
ただ、ここで、ちょっと困りました。ALBって各EC2にヘルスチェックをしている時って、どのようにリクエスト(サーバ名を何で送っているのか)知見がありませんでした そこで、アクセスログに出力を追加しました。
/etc/httpd/conf/httpd.conf の
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
に%Vを付けてアクセスログにサーバ名をログに出すようにしました。
LogFormat "%V %h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
httpdサービスを再起動し、ログを確認します。
10.0.0.1 AAA.BBB.CCC.111 - - [07/Jun/2019:18:08:40 +0900] "GET / HTTP/1.1" 400 52 "-" "ELB-HealthChecker/2.0" 10.0.0.1 AAA.BBB.DDD.222 - - [07/Jun/2019:18:08:58 +0900] "GET / HTTP/1.1" 400 52 "-" "ELB-HealthChecker/2.0" ※AAA.BBB.CCC.111, AAA.BBB.DDD.222はALBのアドレスです
LogFormat のフォーマットはこちら https://httpd.apache.org/docs/2.4/ja/mod/mod_log_config.html#formats
対応
さて、あとは、制限がかかっている原因を解消するのですが、DrupalではTrusted Host settingsでリクエストされるホストヘッダをチェックして 制限をかけているようです。
https://www.drupal.org/docs/8/install/trusted-host-settings
ということで、該当の設定部分を更新します。
setting.phpを変更します。
$settings['trusted_host_patterns'] = array( '^www\.exsample\.com$', '^10\.0\.0\.1' <<== これを追加 );
ここで、通常のホスト名とローカルで割り当てられるIPアドレスを設定しました。
で、OKっと行こうとしたんですが、チェックがはいりました。orz
そう、スケールアウトしたら新しいIPアドレスがEC2に設定されます。これじゃまずいです。
かといって、全部列挙するわけにもいかない。
つ 正規表現
ということで、正規表現の知見を頂きましたので、割り当てられるIPアドレス範囲を設定することにしました
$settings['trusted_host_patterns'] = array( '^www\.exsample\.com$', '^10\.0\.0\.\d{1,3}$' <<== これを追加 );
ただ、もっと厳密にするなら、起動時に、インスタンスメタデータでIPアドレスを取得して、setting.phpを更新するのがいいのではないかなと思います。
$ curl -q http://169.254.169.254/latest/meta-data/public-ipv4 10.0.0.1
参考
+1 対応
トップでもいいのですが、負荷低減のために Drupalにヘルスチェック用のモジュールを導入して、
https://www.drupal.org/project/health_check
ヘルスチェックのパスを
/health
としました。
あとは、ALBのヘルスチェックのパスを変更すれば、Healthyになるはずです! (ダメな時は再度ログからトライします!)
まとめ
ピンポイントで「The provided host name is not valid for this server.」のエラーでないと役にたたないかと思いますが 同じエラーが出た人の役にたてれば幸いです。